/**
 * @HttpLoggingHandler.java  COPYRIGHT xiaojian
 * 
 * Created on 2019年11月14日 下午2:30:58
 */
package xj.toolkit.netty.handler.codec.http;

import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.logging.LogLevel;
import io.netty.util.internal.logging.InternalLogLevel;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

/**
 * @author <a href="mailto:cxj2000@gmail.com">xiaojian.cao</a>
 *
 */
@Sharable
public class HttpHeaderLoggingHandler extends ChannelDuplexHandler {

	private static final LogLevel DEFAULT_LEVEL = LogLevel.DEBUG;

	protected final InternalLogger logger;
	protected final InternalLogLevel internalLevel;

	private final LogLevel level;

	/**
	 * Creates a new instance whose logger name is the fully qualified class name of
	 * the instance with hex dump enabled.
	 */
	public HttpHeaderLoggingHandler() {
		this(DEFAULT_LEVEL);
	}

	/**
	 * Creates a new instance whose logger name is the fully qualified class name of
	 * the instance.
	 *
	 * @param level
	 *            the log level
	 */
	public HttpHeaderLoggingHandler(LogLevel level) {
		if (level == null) {
			throw new NullPointerException("level");
		}

		logger = InternalLoggerFactory.getInstance(getClass());
		this.level = level;
		internalLevel = level.toInternalLevel();
	}

	/**
	 * Returns the {@link LogLevel} that this handler uses to log
	 */
	public LogLevel level() {
		return level;
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if (!(msg instanceof FullHttpRequest)) {
			logger.warn("received msg {} is not FullHttpRequest", msg);
			return;
		}

		if (logger.isEnabled(internalLevel)) {
			logger.log(internalLevel, formatSimple(ctx, "READ", msg));
		}
		ctx.fireChannelRead(msg);
	}

	@Override
	public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
		if (!(msg instanceof FullHttpResponse)) {
			logger.info("received msg {} is not FullHttpResponse", msg);
			return;
		}

		if (logger.isEnabled(internalLevel)) {
			logger.log(internalLevel, formatSimple(ctx, "WRITE", msg));
		}
		ctx.write(msg, promise);
	}

	@Override
	public void flush(ChannelHandlerContext ctx) throws Exception {
		if (logger.isEnabled(internalLevel)) {
			logger.log(internalLevel, format(ctx, "FLUSH"));
		}
		ctx.flush();
	}

	/**
	 * Formats an event and returns the formatted message.
	 *
	 * @param eventName
	 *            the name of the event
	 */
	protected String format(ChannelHandlerContext ctx, String eventName) {
		String chStr = ctx.channel().toString();
		return new StringBuilder(chStr.length() + 1 + eventName.length()).append(chStr).append(' ').append(eventName)
				.toString();
	}

	/**
	 * Generates the default log message of the specified event whose argument is an
	 * arbitrary object.
	 */
	private static String formatSimple(ChannelHandlerContext ctx, String eventName, Object msg) {
		String chStr = ctx.channel().toString();
		String msgStr = String.valueOf(msg);
		StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length());
		return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString();
	}
}
